/*
 * Copyright 2023 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#include <rm/rpc.h>

#include "priv.h"

#include <core/pci.h>
#include <subdev/pci/priv.h>
#include <subdev/timer.h>
#include <subdev/vfn.h>
#include <engine/fifo/chan.h>
#include <engine/sec2.h>
#include <nvif/log.h>

#include <nvfw/fw.h>

#include "nvrm/gsp.h"
#include "nvrm/rpcfn.h"
#include "nvrm/msgfn.h"
#include "nvrm/event.h"
#include "nvrm/fifo.h"

#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/parser.h>

extern struct dentry *nouveau_debugfs_root;

static void
r535_gsp_msgq_work(struct work_struct *work)
{
	struct nvkm_gsp *gsp = container_of(work, typeof(*gsp), msgq.work);

	mutex_lock(&gsp->cmdq.mutex);
	if (*gsp->msgq.rptr != *gsp->msgq.wptr)
		r535_gsp_msg_recv(gsp, 0, 0);
	mutex_unlock(&gsp->cmdq.mutex);
}

static irqreturn_t
r535_gsp_intr(struct nvkm_inth *inth)
{
	struct nvkm_gsp *gsp = container_of(inth, typeof(*gsp), subdev.inth);
	struct nvkm_subdev *subdev = &gsp->subdev;
	u32 intr = nvkm_falcon_rd32(&gsp->falcon, 0x0008);
	u32 inte = nvkm_falcon_rd32(&gsp->falcon, gsp->falcon.func->addr2 +
						  gsp->falcon.func->riscv_irqmask);
	u32 stat = intr & inte;

	if (!stat) {
		nvkm_debug(subdev, "inte %08x %08x\n", intr, inte);
		return IRQ_NONE;
	}

	if (stat & 0x00000040) {
		nvkm_falcon_wr32(&gsp->falcon, 0x004, 0x00000040);
		schedule_work(&gsp->msgq.work);
		stat &= ~0x00000040;
	}

	if (stat) {
		nvkm_error(subdev, "intr %08x\n", stat);
		nvkm_falcon_wr32(&gsp->falcon, 0x014, stat);
		nvkm_falcon_wr32(&gsp->falcon, 0x004, stat);
	}

	nvkm_falcon_intr_retrigger(&gsp->falcon);
	return IRQ_HANDLED;
}

static bool
r535_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int *pinst)
{
	switch (mc_engine_idx) {
	case MC_ENGINE_IDX_GSP:
		*ptype = NVKM_SUBDEV_GSP;
		*pinst = 0;
		return true;
	case MC_ENGINE_IDX_DISP:
		*ptype = NVKM_ENGINE_DISP;
		*pinst = 0;
		return true;
	case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE9:
		*ptype = NVKM_ENGINE_CE;
		*pinst = mc_engine_idx - MC_ENGINE_IDX_CE0;
		return true;
	case MC_ENGINE_IDX_GR0:
		*ptype = NVKM_ENGINE_GR;
		*pinst = 0;
		return true;
	case MC_ENGINE_IDX_NVDEC0 ... MC_ENGINE_IDX_NVDEC7:
		*ptype = NVKM_ENGINE_NVDEC;
		*pinst = mc_engine_idx - MC_ENGINE_IDX_NVDEC0;
		return true;
	case MC_ENGINE_IDX_MSENC ... MC_ENGINE_IDX_MSENC2:
		*ptype = NVKM_ENGINE_NVENC;
		*pinst = mc_engine_idx - MC_ENGINE_IDX_MSENC;
		return true;
	case MC_ENGINE_IDX_NVJPEG0 ... MC_ENGINE_IDX_NVJPEG7:
		*ptype = NVKM_ENGINE_NVJPG;
		*pinst = mc_engine_idx - MC_ENGINE_IDX_NVJPEG0;
		return true;
	case MC_ENGINE_IDX_OFA0:
		*ptype = NVKM_ENGINE_OFA;
		*pinst = 0;
		return true;
	default:
		return false;
	}
}

static int
r535_gsp_intr_get_table(struct nvkm_gsp *gsp)
{
	NV2080_CTRL_INTERNAL_INTR_GET_KERNEL_TABLE_PARAMS *ctrl;
	const struct nvkm_rm_api *rmapi = gsp->rm->api;
	int ret = 0;

	ctrl = nvkm_gsp_rm_ctrl_get(&gsp->internal.device.subdevice,
				    NV2080_CTRL_CMD_INTERNAL_INTR_GET_KERNEL_TABLE, sizeof(*ctrl));
	if (IS_ERR(ctrl))
		return PTR_ERR(ctrl);

	ret = nvkm_gsp_rm_ctrl_push(&gsp->internal.device.subdevice, &ctrl, sizeof(*ctrl));
	if (WARN_ON(ret)) {
		nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
		return ret;
	}

	for (unsigned i = 0; i < ctrl->tableLen; i++) {
		enum nvkm_subdev_type type;
		int inst;

		nvkm_debug(&gsp->subdev,
			   "%2d: engineIdx %3d pmcIntrMask %08x stall %08x nonStall %08x\n", i,
			   ctrl->table[i].engineIdx, ctrl->table[i].pmcIntrMask,
			   ctrl->table[i].vectorStall, ctrl->table[i].vectorNonStall);

		if (!rmapi->gsp->xlat_mc_engine_idx(ctrl->table[i].engineIdx, &type, &inst))
			continue;

		if (WARN_ON(gsp->intr_nr == ARRAY_SIZE(gsp->intr))) {
			ret = -ENOSPC;
			break;
		}

		gsp->intr[gsp->intr_nr].type = type;
		gsp->intr[gsp->intr_nr].inst = inst;
		gsp->intr[gsp->intr_nr].stall = ctrl->table[i].vectorStall;
		gsp->intr[gsp->intr_nr].nonstall = ctrl->table[i].vectorNonStall;
		gsp->intr_nr++;
	}

	nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
	return ret;
}

void
r535_gsp_get_static_info_fb(struct nvkm_gsp *gsp,
			    const struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS *info)
{
	int last_usable = -1;

	for (int i = 0; i < info->numFBRegions; i++) {
		const NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO *reg = &info->fbRegion[i];

		nvkm_debug(&gsp->subdev, "fb region %d: "
			   "%016llx-%016llx rsvd:%016llx perf:%08x comp:%d iso:%d prot:%d\n", i,
			   reg->base, reg->limit, reg->reserved, reg->performance,
			   reg->supportCompressed, reg->supportISO, reg->bProtected);

		if (!reg->reserved && !reg->bProtected) {
			if (reg->supportCompressed && reg->supportISO &&
			    !WARN_ON_ONCE(gsp->fb.region_nr >= ARRAY_SIZE(gsp->fb.region))) {
					const u64 size = (reg->limit + 1) - reg->base;

					gsp->fb.region[gsp->fb.region_nr].addr = reg->base;
					gsp->fb.region[gsp->fb.region_nr].size = size;
					gsp->fb.region_nr++;
			}

			last_usable = i;
		}
	}

	if (last_usable >= 0) {
		u32 rsvd_base = info->fbRegion[last_usable].limit + 1;

		gsp->fb.rsvd_size = gsp->fb.heap.addr - rsvd_base;
	}
}

static int
r535_gsp_get_static_info(struct nvkm_gsp *gsp)
{
	GspStaticConfigInfo *rpc;

	rpc = nvkm_gsp_rpc_rd(gsp, NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO, sizeof(*rpc));
	if (IS_ERR(rpc))
		return PTR_ERR(rpc);

	gsp->internal.client.object.client = &gsp->internal.client;
	gsp->internal.client.object.parent = NULL;
	gsp->internal.client.object.handle = rpc->hInternalClient;
	gsp->internal.client.gsp = gsp;

	gsp->internal.device.object.client = &gsp->internal.client;
	gsp->internal.device.object.parent = &gsp->internal.client.object;
	gsp->internal.device.object.handle = rpc->hInternalDevice;

	gsp->internal.device.subdevice.client = &gsp->internal.client;
	gsp->internal.device.subdevice.parent = &gsp->internal.device.object;
	gsp->internal.device.subdevice.handle = rpc->hInternalSubdevice;

	gsp->bar.rm_bar1_pdb = rpc->bar1PdeBase;
	gsp->bar.rm_bar2_pdb = rpc->bar2PdeBase;

	r535_gsp_get_static_info_fb(gsp, &rpc->fbRegionInfoParams);

	for (int gpc = 0; gpc < ARRAY_SIZE(rpc->tpcInfo); gpc++) {
		if (rpc->gpcInfo.gpcMask & BIT(gpc)) {
			gsp->gr.tpcs += hweight32(rpc->tpcInfo[gpc].tpcMask);
			gsp->gr.gpcs++;
		}
	}

	nvkm_gsp_rpc_done(gsp, rpc);
	return 0;
}

void
nvkm_gsp_mem_dtor(struct nvkm_gsp_mem *mem)
{
	if (mem->data) {
		/*
		 * Poison the buffer to catch any unexpected access from
		 * GSP-RM if the buffer was prematurely freed.
		 */
		memset(mem->data, 0xFF, mem->size);

		dma_free_coherent(mem->dev, mem->size, mem->data, mem->addr);
		put_device(mem->dev);

		memset(mem, 0, sizeof(*mem));
	}
}

/**
 * nvkm_gsp_mem_ctor - constructor for nvkm_gsp_mem objects
 * @gsp: gsp pointer
 * @size: number of bytes to allocate
 * @mem: nvkm_gsp_mem object to initialize
 *
 * Allocates a block of memory for use with GSP.
 *
 * This memory block can potentially out-live the driver's remove() callback,
 * so we take a device reference to ensure its lifetime. The reference is
 * dropped in the destructor.
 */
int
nvkm_gsp_mem_ctor(struct nvkm_gsp *gsp, size_t size, struct nvkm_gsp_mem *mem)
{
	mem->data = dma_alloc_coherent(gsp->subdev.device->dev, size, &mem->addr, GFP_KERNEL);
	if (WARN_ON(!mem->data))
		return -ENOMEM;

	mem->size = size;
	mem->dev = get_device(gsp->subdev.device->dev);

	return 0;
}

static int
r535_gsp_postinit(struct nvkm_gsp *gsp)
{
	struct nvkm_device *device = gsp->subdev.device;
	const struct nvkm_rm_api *rmapi = gsp->rm->api;
	int ret;

	ret = rmapi->gsp->get_static_info(gsp);
	if (WARN_ON(ret))
		return ret;

	INIT_WORK(&gsp->msgq.work, r535_gsp_msgq_work);

	ret = r535_gsp_intr_get_table(gsp);
	if (WARN_ON(ret))
		return ret;

	ret = nvkm_gsp_intr_stall(gsp, gsp->subdev.type, gsp->subdev.inst);
	if (WARN_ON(ret < 0))
		return ret;

	ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &gsp->subdev,
			    r535_gsp_intr, &gsp->subdev.inth);
	if (WARN_ON(ret))
		return ret;

	nvkm_inth_allow(&gsp->subdev.inth);
	nvkm_wr32(device, 0x110004, 0x00000040);

	/* Release the DMA buffers that were needed only for boot and init */
	nvkm_gsp_mem_dtor(&gsp->boot.fw);
	nvkm_gsp_mem_dtor(&gsp->libos);

	return ret;
}

static int
r535_gsp_rpc_unloading_guest_driver(struct nvkm_gsp *gsp, bool suspend)
{
	rpc_unloading_guest_driver_v1F_07 *rpc;

	rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER, sizeof(*rpc));
	if (IS_ERR(rpc))
		return PTR_ERR(rpc);

	if (suspend) {
		rpc->bInPMTransition = 1;
		rpc->bGc6Entering = 0;
		rpc->newLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3;
	} else {
		rpc->bInPMTransition = 0;
		rpc->bGc6Entering = 0;
		rpc->newLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_0;
	}

	return nvkm_gsp_rpc_wr(gsp, rpc, NVKM_GSP_RPC_REPLY_RECV);
}

enum registry_type {
	REGISTRY_TABLE_ENTRY_TYPE_DWORD  = 1, /* 32-bit unsigned integer */
	REGISTRY_TABLE_ENTRY_TYPE_BINARY = 2, /* Binary blob */
	REGISTRY_TABLE_ENTRY_TYPE_STRING = 3, /* Null-terminated string */
};

/* An arbitrary limit to the length of a registry key */
#define REGISTRY_MAX_KEY_LENGTH		64

/**
 * struct registry_list_entry - linked list member for a registry key/value
 * @head: list_head struct
 * @type: dword, binary, or string
 * @klen: the length of name of the key
 * @vlen: the length of the value
 * @key: the key name
 * @dword: the data, if REGISTRY_TABLE_ENTRY_TYPE_DWORD
 * @binary: the data, if TYPE_BINARY or TYPE_STRING
 *
 * Every registry key/value is represented internally by this struct.
 *
 * Type DWORD is a simple 32-bit unsigned integer, and its value is stored in
 * @dword.
 *
 * Types BINARY and STRING are variable-length binary blobs.  The only real
 * difference between BINARY and STRING is that STRING is null-terminated and
 * is expected to contain only printable characters.
 *
 * Note: it is technically possible to have multiple keys with the same name
 * but different types, but this is not useful since GSP-RM expects keys to
 * have only one specific type.
 */
struct registry_list_entry {
	struct list_head head;
	enum registry_type type;
	size_t klen;
	char key[REGISTRY_MAX_KEY_LENGTH];
	size_t vlen;
	u32 dword;			/* TYPE_DWORD */
	u8 binary[] __counted_by(vlen);	/* TYPE_BINARY or TYPE_STRING */
};

/**
 * add_registry -- adds a registry entry
 * @gsp: gsp pointer
 * @key: name of the registry key
 * @type: type of data
 * @data: pointer to value
 * @length: size of data, in bytes
 *
 * Adds a registry key/value pair to the registry database.
 *
 * This function collects the registry information in a linked list.  After
 * all registry keys have been added, build_registry() is used to create the
 * RPC data structure.
 *
 * registry_rpc_size is a running total of the size of all registry keys.
 * It's used to avoid an O(n) calculation of the size when the RPC is built.
 *
 * Returns 0 on success, or negative error code on error.
 */
static int add_registry(struct nvkm_gsp *gsp, const char *key,
			enum registry_type type, const void *data, size_t length)
{
	struct registry_list_entry *reg;
	const size_t nlen = strnlen(key, REGISTRY_MAX_KEY_LENGTH) + 1;
	size_t alloc_size; /* extra bytes to alloc for binary or string value */

	if (nlen > REGISTRY_MAX_KEY_LENGTH)
		return -EINVAL;

	alloc_size = (type == REGISTRY_TABLE_ENTRY_TYPE_DWORD) ? 0 : length;

	reg = kmalloc(sizeof(*reg) + alloc_size, GFP_KERNEL);
	if (!reg)
		return -ENOMEM;

	switch (type) {
	case REGISTRY_TABLE_ENTRY_TYPE_DWORD:
		reg->dword = *(const u32 *)(data);
		break;
	case REGISTRY_TABLE_ENTRY_TYPE_BINARY:
	case REGISTRY_TABLE_ENTRY_TYPE_STRING:
		memcpy(reg->binary, data, alloc_size);
		break;
	default:
		nvkm_error(&gsp->subdev, "unrecognized registry type %u for '%s'\n",
			   type, key);
		kfree(reg);
		return -EINVAL;
	}

	memcpy(reg->key, key, nlen);
	reg->klen = nlen;
	reg->vlen = length;
	reg->type = type;

	list_add_tail(&reg->head, &gsp->registry_list);
	gsp->registry_rpc_size += sizeof(PACKED_REGISTRY_ENTRY) + nlen + alloc_size;

	return 0;
}

static int add_registry_num(struct nvkm_gsp *gsp, const char *key, u32 value)
{
	return add_registry(gsp, key, REGISTRY_TABLE_ENTRY_TYPE_DWORD,
			    &value, sizeof(u32));
}

static int add_registry_string(struct nvkm_gsp *gsp, const char *key, const char *value)
{
	return add_registry(gsp, key, REGISTRY_TABLE_ENTRY_TYPE_STRING,
			    value, strlen(value) + 1);
}

/**
 * build_registry -- create the registry RPC data
 * @gsp: gsp pointer
 * @registry: pointer to the RPC payload to fill
 *
 * After all registry key/value pairs have been added, call this function to
 * build the RPC.
 *
 * The registry RPC looks like this:
 *
 * +-----------------+
 * |NvU32 size;      |
 * |NvU32 numEntries;|
 * +-----------------+
 * +----------------------------------------+
 * |PACKED_REGISTRY_ENTRY                   |
 * +----------------------------------------+
 * |Null-terminated key (string) for entry 0|
 * +----------------------------------------+
 * |Binary/string data value for entry 0    | (only if necessary)
 * +----------------------------------------+
 *
 * +----------------------------------------+
 * |PACKED_REGISTRY_ENTRY                   |
 * +----------------------------------------+
 * |Null-terminated key (string) for entry 1|
 * +----------------------------------------+
 * |Binary/string data value for entry 1    | (only if necessary)
 * +----------------------------------------+
 * ... (and so on, one copy for each entry)
 *
 *
 * The 'data' field of an entry is either a 32-bit integer (for type DWORD)
 * or an offset into the PACKED_REGISTRY_TABLE (for types BINARY and STRING).
 *
 * All memory allocated by add_registry() is released.
 */
static void build_registry(struct nvkm_gsp *gsp, PACKED_REGISTRY_TABLE *registry)
{
	struct registry_list_entry *reg, *n;
	size_t str_offset;
	unsigned int i = 0;

	registry->numEntries = list_count_nodes(&gsp->registry_list);
	str_offset = struct_size(registry, entries, registry->numEntries);

	list_for_each_entry_safe(reg, n, &gsp->registry_list, head) {
		registry->entries[i].type = reg->type;
		registry->entries[i].length = reg->vlen;

		/* Append the key name to the table */
		registry->entries[i].nameOffset = str_offset;
		memcpy((void *)registry + str_offset, reg->key, reg->klen);
		str_offset += reg->klen;

		switch (reg->type) {
		case REGISTRY_TABLE_ENTRY_TYPE_DWORD:
			registry->entries[i].data = reg->dword;
			break;
		case REGISTRY_TABLE_ENTRY_TYPE_BINARY:
		case REGISTRY_TABLE_ENTRY_TYPE_STRING:
			/* If the type is binary or string, also append the value */
			memcpy((void *)registry + str_offset, reg->binary, reg->vlen);
			registry->entries[i].data = str_offset;
			str_offset += reg->vlen;
			break;
		default:
			break;
		}

		i++;
		list_del(&reg->head);
		kfree(reg);
	}

	/* Double-check that we calculated the sizes correctly */
	WARN_ON(gsp->registry_rpc_size != str_offset);

	registry->size = gsp->registry_rpc_size;
}

/**
 * clean_registry -- clean up registry memory in case of error
 * @gsp: gsp pointer
 *
 * Call this function to clean up all memory allocated by add_registry()
 * in case of error and build_registry() is not called.
 */
static void clean_registry(struct nvkm_gsp *gsp)
{
	struct registry_list_entry *reg, *n;

	list_for_each_entry_safe(reg, n, &gsp->registry_list, head) {
		list_del(&reg->head);
		kfree(reg);
	}

	gsp->registry_rpc_size = sizeof(PACKED_REGISTRY_TABLE);
}

MODULE_PARM_DESC(NVreg_RegistryDwords,
		 "A semicolon-separated list of key=integer pairs of GSP-RM registry keys");
static char *NVreg_RegistryDwords;
module_param(NVreg_RegistryDwords, charp, 0400);

/* dword only */
struct nv_gsp_registry_entries {
	const char *name;
	u32 value;
};

/*
 * r535_registry_entries - required registry entries for GSP-RM
 *
 * This array lists registry entries that are required for GSP-RM to
 * function correctly.
 *
 * RMSecBusResetEnable - enables PCI secondary bus reset
 * RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration
 *   registers on any PCI reset.
 * RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID
 *   is not found in the internal product name database.
 */
static const struct nv_gsp_registry_entries r535_registry_entries[] = {
	{ "RMSecBusResetEnable", 1 },
	{ "RMForcePcieConfigSave", 1 },
	{ "RMDevidCheckIgnore", 1 },
};
#define NV_GSP_REG_NUM_ENTRIES ARRAY_SIZE(r535_registry_entries)

/**
 * strip - strips all characters in 'reject' from 's'
 * @s: string to strip
 * @reject: string of characters to remove
 *
 * 's' is modified.
 *
 * Returns the length of the new string.
 */
static size_t strip(char *s, const char *reject)
{
	char *p = s, *p2 = s;
	size_t length = 0;
	char c;

	do {
		while ((c = *p2) && strchr(reject, c))
			p2++;

		*p++ = c = *p2++;
		length++;
	} while (c);

	return length;
}

/**
 * r535_gsp_rpc_set_registry - build registry RPC and call GSP-RM
 * @gsp: gsp pointer
 *
 * The GSP-RM registry is a set of key/value pairs that configure some aspects
 * of GSP-RM. The keys are strings, and the values are 32-bit integers.
 *
 * The registry is built from a combination of a static hard-coded list (see
 * above) and entries passed on the driver's command line.
 */
static int
r535_gsp_rpc_set_registry(struct nvkm_gsp *gsp)
{
	PACKED_REGISTRY_TABLE *rpc;
	unsigned int i;
	int ret;

	INIT_LIST_HEAD(&gsp->registry_list);
	gsp->registry_rpc_size = sizeof(PACKED_REGISTRY_TABLE);

	for (i = 0; i < NV_GSP_REG_NUM_ENTRIES; i++) {
		ret = add_registry_num(gsp, r535_registry_entries[i].name,
				       r535_registry_entries[i].value);
		if (ret)
			goto fail;
	}

	/*
	 * The NVreg_RegistryDwords parameter is a string of key=value
	 * pairs separated by semicolons. We need to extract and trim each
	 * substring, and then parse the substring to extract the key and
	 * value.
	 */
	if (NVreg_RegistryDwords) {
		char *p = kstrdup(NVreg_RegistryDwords, GFP_KERNEL);
		char *start, *next = p, *equal;

		if (!p) {
			ret = -ENOMEM;
			goto fail;
		}

		/* Remove any whitespace from the parameter string */
		strip(p, " \t\n");

		while ((start = strsep(&next, ";"))) {
			long value;

			equal = strchr(start, '=');
			if (!equal || equal == start || equal[1] == 0) {
				nvkm_error(&gsp->subdev,
					   "ignoring invalid registry string '%s'\n",
					   start);
				continue;
			}

			/* Truncate the key=value string to just key */
			*equal = 0;

			ret = kstrtol(equal + 1, 0, &value);
			if (!ret) {
				ret = add_registry_num(gsp, start, value);
			} else {
				/* Not a number, so treat it as a string */
				ret = add_registry_string(gsp, start, equal + 1);
			}

			if (ret) {
				nvkm_error(&gsp->subdev,
					   "ignoring invalid registry key/value '%s=%s'\n",
					   start, equal + 1);
				continue;
			}
		}

		kfree(p);
	}

	rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_SET_REGISTRY, gsp->registry_rpc_size);
	if (IS_ERR(rpc)) {
		ret = PTR_ERR(rpc);
		goto fail;
	}

	build_registry(gsp, rpc);

	return nvkm_gsp_rpc_wr(gsp, rpc, NVKM_GSP_RPC_REPLY_NOWAIT);

fail:
	clean_registry(gsp);
	return ret;
}

#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
void
r535_gsp_acpi_caps(acpi_handle handle, CAPS_METHOD_DATA *caps)
{
	const guid_t NVOP_DSM_GUID =
		GUID_INIT(0xA486D8F8, 0x0BDA, 0x471B,
			  0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0);
	u64 NVOP_DSM_REV = 0x00000100;
	union acpi_object argv4 = {
		.buffer.type    = ACPI_TYPE_BUFFER,
		.buffer.length  = 4,
	}, *obj;

	caps->status = 0xffff;

	if (!acpi_check_dsm(handle, &NVOP_DSM_GUID, NVOP_DSM_REV, BIT_ULL(0x1a)))
		return;

	argv4.buffer.pointer = kmalloc(argv4.buffer.length, GFP_KERNEL);
	if (!argv4.buffer.pointer)
		return;

	obj = acpi_evaluate_dsm(handle, &NVOP_DSM_GUID, NVOP_DSM_REV, 0x1a, &argv4);
	if (!obj)
		goto done;

	if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) ||
	    WARN_ON(obj->buffer.length != 4))
		goto done;

	caps->status = 0;
	caps->optimusCaps = *(u32 *)obj->buffer.pointer;

done:
	ACPI_FREE(obj);

	kfree(argv4.buffer.pointer);
}

void
r535_gsp_acpi_jt(acpi_handle handle, JT_METHOD_DATA *jt)
{
	const guid_t JT_DSM_GUID =
		GUID_INIT(0xCBECA351L, 0x067B, 0x4924,
			  0x9C, 0xBD, 0xB4, 0x6B, 0x00, 0xB8, 0x6F, 0x34);
	u64 JT_DSM_REV = 0x00000103;
	u32 caps;
	union acpi_object argv4 = {
		.buffer.type    = ACPI_TYPE_BUFFER,
		.buffer.length  = sizeof(caps),
	}, *obj;

	jt->status = 0xffff;

	argv4.buffer.pointer = kmalloc(argv4.buffer.length, GFP_KERNEL);
	if (!argv4.buffer.pointer)
		return;

	obj = acpi_evaluate_dsm(handle, &JT_DSM_GUID, JT_DSM_REV, 0x1, &argv4);
	if (!obj)
		goto done;

	if (WARN_ON(obj->type != ACPI_TYPE_BUFFER) ||
	    WARN_ON(obj->buffer.length != 4))
		goto done;

	jt->status = 0;
	jt->jtCaps = *(u32 *)obj->buffer.pointer;
	jt->jtRevId = (jt->jtCaps & 0xfff00000) >> 20;
	jt->bSBIOSCaps = 0;

done:
	ACPI_FREE(obj);

	kfree(argv4.buffer.pointer);
}

static void
r535_gsp_acpi_mux_id(acpi_handle handle, u32 id, MUX_METHOD_DATA_ELEMENT *mode,
						 MUX_METHOD_DATA_ELEMENT *part)
{
	union acpi_object mux_arg = { ACPI_TYPE_INTEGER };
	struct acpi_object_list input = { 1, &mux_arg };
	acpi_handle iter = NULL, handle_mux = NULL;
	acpi_status status;
	unsigned long long value;

	mode->status = 0xffff;
	part->status = 0xffff;

	do {
		status = acpi_get_next_object(ACPI_TYPE_DEVICE, handle, iter, &iter);
		if (ACPI_FAILURE(status) || !iter)
			return;

		status = acpi_evaluate_integer(iter, "_ADR", NULL, &value);
		if (ACPI_FAILURE(status) || value != id)
			continue;

		handle_mux = iter;
	} while (!handle_mux);

	if (!handle_mux)
		return;

	/* I -think- 0 means "acquire" according to nvidia's driver source */
	input.pointer->integer.type = ACPI_TYPE_INTEGER;
	input.pointer->integer.value = 0;

	status = acpi_evaluate_integer(handle_mux, "MXDM", &input, &value);
	if (ACPI_SUCCESS(status)) {
		mode->acpiId = id;
		mode->mode   = value;
		mode->status = 0;
	}

	status = acpi_evaluate_integer(handle_mux, "MXDS", &input, &value);
	if (ACPI_SUCCESS(status)) {
		part->acpiId = id;
		part->mode   = value;
		part->status = 0;
	}
}

static void
r535_gsp_acpi_mux(acpi_handle handle, DOD_METHOD_DATA *dod, MUX_METHOD_DATA *mux)
{
	mux->tableLen = dod->acpiIdListLen / sizeof(dod->acpiIdList[0]);

	for (int i = 0; i < mux->tableLen; i++) {
		r535_gsp_acpi_mux_id(handle, dod->acpiIdList[i], &mux->acpiIdMuxModeTable[i],
								 &mux->acpiIdMuxPartTable[i]);
	}
}

void
r535_gsp_acpi_dod(acpi_handle handle, DOD_METHOD_DATA *dod)
{
	acpi_status status;
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *_DOD;

	dod->status = 0xffff;

	status = acpi_evaluate_object(handle, "_DOD", NULL, &output);
	if (ACPI_FAILURE(status))
		return;

	_DOD = output.pointer;

	if (WARN_ON(_DOD->type != ACPI_TYPE_PACKAGE) ||
	    WARN_ON(_DOD->package.count > ARRAY_SIZE(dod->acpiIdList)))
		return;

	for (int i = 0; i < _DOD->package.count; i++) {
		if (WARN_ON(_DOD->package.elements[i].type != ACPI_TYPE_INTEGER))
			return;

		dod->acpiIdList[i] = _DOD->package.elements[i].integer.value;
		dod->acpiIdListLen += sizeof(dod->acpiIdList[0]);
	}

	dod->status = 0;
	kfree(output.pointer);
}
#endif

static void
r535_gsp_acpi_info(struct nvkm_gsp *gsp, ACPI_METHOD_DATA *acpi)
{
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
	acpi_handle handle = ACPI_HANDLE(gsp->subdev.device->dev);

	if (!handle)
		return;

	acpi->bValid = 1;

	r535_gsp_acpi_dod(handle, &acpi->dodMethodData);
	if (acpi->dodMethodData.status == 0)
		r535_gsp_acpi_mux(handle, &acpi->dodMethodData, &acpi->muxMethodData);

	r535_gsp_acpi_jt(handle, &acpi->jtMethodData);
	r535_gsp_acpi_caps(handle, &acpi->capsMethodData);
#endif
}

static int
r535_gsp_set_system_info(struct nvkm_gsp *gsp)
{
	struct nvkm_device *device = gsp->subdev.device;
	struct nvkm_device_pci *pdev = container_of(device, typeof(*pdev), device);
	GspSystemInfo *info;

	if (WARN_ON(device->type == NVKM_DEVICE_TEGRA))
		return -ENOSYS;

	info = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO, sizeof(*info));
	if (IS_ERR(info))
		return PTR_ERR(info);

	info->gpuPhysAddr = device->func->resource_addr(device, NVKM_BAR0_PRI);
	info->gpuPhysFbAddr = device->func->resource_addr(device, NVKM_BAR1_FB);
	info->gpuPhysInstAddr = device->func->resource_addr(device, NVKM_BAR2_INST);
	info->nvDomainBusDeviceFunc = pci_dev_id(pdev->pdev);
	info->maxUserVa = TASK_SIZE;
	info->pciConfigMirrorBase = device->pci->func->cfg.addr;
	info->pciConfigMirrorSize = device->pci->func->cfg.size;
	r535_gsp_acpi_info(gsp, &info->acpiMethodData);

	return nvkm_gsp_rpc_wr(gsp, info, NVKM_GSP_RPC_REPLY_NOWAIT);
}

static int
r535_gsp_msg_os_error_log(void *priv, u32 fn, void *repv, u32 repc)
{
	struct nvkm_gsp *gsp = priv;
	struct nvkm_subdev *subdev = &gsp->subdev;
	rpc_os_error_log_v17_00 *msg = repv;

	if (WARN_ON(repc < sizeof(*msg)))
		return -EINVAL;

	nvkm_error(subdev, "Xid:%d %s\n", msg->exceptType, msg->errString);
	return 0;
}

static int
r535_gsp_msg_mmu_fault_queued(void *priv, u32 fn, void *repv, u32 repc)
{
	struct nvkm_gsp *gsp = priv;
	struct nvkm_subdev *subdev = &gsp->subdev;

	WARN_ON(repc != 0);

	nvkm_error(subdev, "mmu fault queued\n");
	return 0;
}

static int
r535_gsp_msg_post_event(void *priv, u32 fn, void *repv, u32 repc)
{
	struct nvkm_gsp *gsp = priv;
	struct nvkm_gsp_client *client;
	struct nvkm_subdev *subdev = &gsp->subdev;
	rpc_post_event_v17_00 *msg = repv;

	if (WARN_ON(repc < sizeof(*msg)))
		return -EINVAL;
	if (WARN_ON(repc != sizeof(*msg) + msg->eventDataSize))
		return -EINVAL;

	nvkm_debug(subdev, "event: %08x %08x %d %08x %08x %d %d\n",
		   msg->hClient, msg->hEvent, msg->notifyIndex, msg->data,
		   msg->status, msg->eventDataSize, msg->bNotifyList);

	mutex_lock(&gsp->client_id.mutex);
	client = idr_find(&gsp->client_id.idr, msg->hClient & 0xffff);
	if (client) {
		struct nvkm_gsp_event *event;
		bool handled = false;

		list_for_each_entry(event, &client->events, head) {
			if (event->object.handle == msg->hEvent) {
				event->func(event, msg->eventData, msg->eventDataSize);
				handled = true;
			}
		}

		if (!handled) {
			nvkm_error(subdev, "event: cid 0x%08x event 0x%08x not found!\n",
				   msg->hClient, msg->hEvent);
		}
	} else {
		nvkm_error(subdev, "event: cid 0x%08x not found!\n", msg->hClient);
	}
	mutex_unlock(&gsp->client_id.mutex);
	return 0;
}

/**
 * r535_gsp_msg_run_cpu_sequencer() -- process I/O commands from the GSP
 * @priv: gsp pointer
 * @fn: function number (ignored)
 * @repv: pointer to libos print RPC
 * @repc: message size
 *
 * The GSP sequencer is a list of I/O commands that the GSP can send to
 * the driver to perform for various purposes.  The most common usage is to
 * perform a special mid-initialization reset.
 */
static int
r535_gsp_msg_run_cpu_sequencer(void *priv, u32 fn, void *repv, u32 repc)
{
	struct nvkm_gsp *gsp = priv;
	struct nvkm_subdev *subdev = &gsp->subdev;
	struct nvkm_device *device = subdev->device;
	rpc_run_cpu_sequencer_v17_00 *seq = repv;
	int ptr = 0, ret;

	nvkm_debug(subdev, "seq: %08x %08x\n", seq->bufferSizeDWord, seq->cmdIndex);

	while (ptr < seq->cmdIndex) {
		GSP_SEQUENCER_BUFFER_CMD *cmd = (void *)&seq->commandBuffer[ptr];

		ptr += 1;
		ptr += GSP_SEQUENCER_PAYLOAD_SIZE_DWORDS(cmd->opCode);

		switch (cmd->opCode) {
		case GSP_SEQ_BUF_OPCODE_REG_WRITE: {
			u32 addr = cmd->payload.regWrite.addr;
			u32 data = cmd->payload.regWrite.val;

			nvkm_trace(subdev, "seq wr32 %06x %08x\n", addr, data);
			nvkm_wr32(device, addr, data);
		}
			break;
		case GSP_SEQ_BUF_OPCODE_REG_MODIFY: {
			u32 addr = cmd->payload.regModify.addr;
			u32 mask = cmd->payload.regModify.mask;
			u32 data = cmd->payload.regModify.val;

			nvkm_trace(subdev, "seq mask %06x %08x %08x\n", addr, mask, data);
			nvkm_mask(device, addr, mask, data);
		}
			break;
		case GSP_SEQ_BUF_OPCODE_REG_POLL: {
			u32 addr = cmd->payload.regPoll.addr;
			u32 mask = cmd->payload.regPoll.mask;
			u32 data = cmd->payload.regPoll.val;
			u32 usec = cmd->payload.regPoll.timeout ?: 4000000;
			//u32 error = cmd->payload.regPoll.error;

			nvkm_trace(subdev, "seq poll %06x %08x %08x %d\n", addr, mask, data, usec);
			nvkm_rd32(device, addr);
			nvkm_usec(device, usec,
				if ((nvkm_rd32(device, addr) & mask) == data)
					break;
			);
		}
			break;
		case GSP_SEQ_BUF_OPCODE_DELAY_US: {
			u32 usec = cmd->payload.delayUs.val;

			nvkm_trace(subdev, "seq usec %d\n", usec);
			udelay(usec);
		}
			break;
		case GSP_SEQ_BUF_OPCODE_REG_STORE: {
			u32 addr = cmd->payload.regStore.addr;
			u32 slot = cmd->payload.regStore.index;

			seq->regSaveArea[slot] = nvkm_rd32(device, addr);
			nvkm_trace(subdev, "seq save %08x -> %d: %08x\n", addr, slot,
				   seq->regSaveArea[slot]);
		}
			break;
		case GSP_SEQ_BUF_OPCODE_CORE_RESET:
			nvkm_trace(subdev, "seq core reset\n");
			nvkm_falcon_reset(&gsp->falcon);
			nvkm_falcon_mask(&gsp->falcon, 0x624, 0x00000080, 0x00000080);
			nvkm_falcon_wr32(&gsp->falcon, 0x10c, 0x00000000);
			break;
		case GSP_SEQ_BUF_OPCODE_CORE_START:
			nvkm_trace(subdev, "seq core start\n");
			if (nvkm_falcon_rd32(&gsp->falcon, 0x100) & 0x00000040)
				nvkm_falcon_wr32(&gsp->falcon, 0x130, 0x00000002);
			else
				nvkm_falcon_wr32(&gsp->falcon, 0x100, 0x00000002);
			break;
		case GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT:
			nvkm_trace(subdev, "seq core wait halt\n");
			nvkm_msec(device, 2000,
				if (nvkm_falcon_rd32(&gsp->falcon, 0x100) & 0x00000010)
					break;
			);
			break;
		case GSP_SEQ_BUF_OPCODE_CORE_RESUME: {
			struct nvkm_sec2 *sec2 = device->sec2;
			u32 mbox0;

			nvkm_trace(subdev, "seq core resume\n");

			ret = gsp->func->reset(gsp);
			if (WARN_ON(ret))
				return ret;

			nvkm_falcon_wr32(&gsp->falcon, 0x040, lower_32_bits(gsp->libos.addr));
			nvkm_falcon_wr32(&gsp->falcon, 0x044, upper_32_bits(gsp->libos.addr));

			nvkm_falcon_start(&sec2->falcon);

			if (nvkm_msec(device, 2000,
				if (nvkm_rd32(device, 0x1180f8) & 0x04000000)
					break;
			) < 0)
				return -ETIMEDOUT;

			mbox0 = nvkm_falcon_rd32(&sec2->falcon, 0x040);
			if (WARN_ON(mbox0)) {
				nvkm_error(&gsp->subdev, "seq core resume sec2: 0x%x\n", mbox0);
				return -EIO;
			}

			nvkm_falcon_wr32(&gsp->falcon, 0x080, gsp->boot.app_version);

			if (WARN_ON(!nvkm_falcon_riscv_active(&gsp->falcon)))
				return -EIO;
		}
			break;
		default:
			nvkm_error(subdev, "unknown sequencer opcode %08x\n", cmd->opCode);
			return -EINVAL;
		}
	}

	return 0;
}

static int
r535_gsp_shared_init(struct nvkm_gsp *gsp)
{
	struct {
		msgqTxHeader tx;
		msgqRxHeader rx;
	} *cmdq, *msgq;
	int ret, i;

	gsp->shm.cmdq.size = 0x40000;
	gsp->shm.msgq.size = 0x40000;

	gsp->shm.ptes.nr  = (gsp->shm.cmdq.size + gsp->shm.msgq.size) >> GSP_PAGE_SHIFT;
	gsp->shm.ptes.nr += DIV_ROUND_UP(gsp->shm.ptes.nr * sizeof(u64), GSP_PAGE_SIZE);
	gsp->shm.ptes.size = ALIGN(gsp->shm.ptes.nr * sizeof(u64), GSP_PAGE_SIZE);

	ret = nvkm_gsp_mem_ctor(gsp, gsp->shm.ptes.size +
				     gsp->shm.cmdq.size +
				     gsp->shm.msgq.size,
				&gsp->shm.mem);
	if (ret)
		return ret;

	gsp->shm.ptes.ptr = gsp->shm.mem.data;
	gsp->shm.cmdq.ptr = (u8 *)gsp->shm.ptes.ptr + gsp->shm.ptes.size;
	gsp->shm.msgq.ptr = (u8 *)gsp->shm.cmdq.ptr + gsp->shm.cmdq.size;

	for (i = 0; i < gsp->shm.ptes.nr; i++)
		gsp->shm.ptes.ptr[i] = gsp->shm.mem.addr + (i << GSP_PAGE_SHIFT);

	cmdq = gsp->shm.cmdq.ptr;
	cmdq->tx.version = 0;
	cmdq->tx.size = gsp->shm.cmdq.size;
	cmdq->tx.entryOff = GSP_PAGE_SIZE;
	cmdq->tx.msgSize = GSP_PAGE_SIZE;
	cmdq->tx.msgCount = (cmdq->tx.size - cmdq->tx.entryOff) / cmdq->tx.msgSize;
	cmdq->tx.writePtr = 0;
	cmdq->tx.flags = 1;
	cmdq->tx.rxHdrOff = offsetof(typeof(*cmdq), rx.readPtr);

	msgq = gsp->shm.msgq.ptr;

	gsp->cmdq.cnt = cmdq->tx.msgCount;
	gsp->cmdq.wptr = &cmdq->tx.writePtr;
	gsp->cmdq.rptr = &msgq->rx.readPtr;
	gsp->msgq.cnt = cmdq->tx.msgCount;
	gsp->msgq.wptr = &msgq->tx.writePtr;
	gsp->msgq.rptr = &cmdq->rx.readPtr;
	return 0;
}

static void
r535_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
{
	GSP_ARGUMENTS_CACHED *args = gsp->rmargs.data;

	args->messageQueueInitArguments.sharedMemPhysAddr = gsp->shm.mem.addr;
	args->messageQueueInitArguments.pageTableEntryCount = gsp->shm.ptes.nr;
	args->messageQueueInitArguments.cmdQueueOffset =
		(u8 *)gsp->shm.cmdq.ptr - (u8 *)gsp->shm.mem.data;
	args->messageQueueInitArguments.statQueueOffset =
		(u8 *)gsp->shm.msgq.ptr - (u8 *)gsp->shm.mem.data;

	if (!resume) {
		args->srInitArguments.oldLevel = 0;
		args->srInitArguments.flags = 0;
		args->srInitArguments.bInPMTransition = 0;
	} else {
		args->srInitArguments.oldLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3;
		args->srInitArguments.flags = 0;
		args->srInitArguments.bInPMTransition = 1;
	}
}

static int
r535_gsp_rmargs_init(struct nvkm_gsp *gsp, bool resume)
{
	int ret;

	if (!resume) {
		ret = r535_gsp_shared_init(gsp);
		if (ret)
			return ret;

		ret = nvkm_gsp_mem_ctor(gsp, 0x1000, &gsp->rmargs);
		if (ret)
			return ret;
	}

	gsp->rm->api->gsp->set_rmargs(gsp, resume);
	return 0;
}

#ifdef CONFIG_DEBUG_FS

/*
 * If GSP-RM load fails, then the GSP nvkm object will be deleted, the logging
 * debugfs entries will be deleted, and it will not be possible to debug the
 * load failure. The keep_gsp_logging parameter tells Nouveau to copy the
 * logging buffers to new debugfs entries, and these entries are retained
 * until the driver unloads.
 */
static bool keep_gsp_logging;
module_param(keep_gsp_logging, bool, 0444);
MODULE_PARM_DESC(keep_gsp_logging,
		 "Migrate the GSP-RM logging debugfs entries upon exit");

/*
 * GSP-RM uses a pseudo-class mechanism to define of a variety of per-"engine"
 * data structures, and each engine has a "class ID" genererated by a
 * pre-processor. This is the class ID for the PMU.
 */
#define NV_GSP_MSG_EVENT_UCODE_LIBOS_CLASS_PMU		0xf3d722

/**
 * struct rpc_ucode_libos_print_v1e_08 - RPC payload for libos print buffers
 * @ucode_eng_desc: the engine descriptor
 * @libos_print_buf_size: the size of the libos_print_buf[]
 * @libos_print_buf: the actual buffer
 *
 * The engine descriptor is divided into 31:8 "class ID" and 7:0 "instance
 * ID". We only care about messages from PMU.
 */
struct rpc_ucode_libos_print_v1e_08 {
	u32 ucode_eng_desc;
	u32 libos_print_buf_size;
	u8 libos_print_buf[];
};

/**
 * r535_gsp_msg_libos_print - capture log message from the PMU
 * @priv: gsp pointer
 * @fn: function number (ignored)
 * @repv: pointer to libos print RPC
 * @repc: message size
 *
 * Called when we receive a UCODE_LIBOS_PRINT event RPC from GSP-RM. This RPC
 * contains the contents of the libos print buffer from PMU. It is typically
 * only written to when PMU encounters an error.
 *
 * Technically this RPC can be used to pass print buffers from any number of
 * GSP-RM engines, but we only expect to receive them for the PMU.
 *
 * For the PMU, the buffer is 4K in size and the RPC always contains the full
 * contents.
 */
static int
r535_gsp_msg_libos_print(void *priv, u32 fn, void *repv, u32 repc)
{
	struct nvkm_gsp *gsp = priv;
	struct nvkm_subdev *subdev = &gsp->subdev;
	struct rpc_ucode_libos_print_v1e_08 *rpc = repv;
	unsigned int class = rpc->ucode_eng_desc >> 8;

	nvkm_debug(subdev, "received libos print from class 0x%x for %u bytes\n",
		   class, rpc->libos_print_buf_size);

	if (class != NV_GSP_MSG_EVENT_UCODE_LIBOS_CLASS_PMU) {
		nvkm_warn(subdev,
			  "received libos print from unknown class 0x%x\n",
			  class);
		return -ENOMSG;
	}

	if (rpc->libos_print_buf_size > GSP_PAGE_SIZE) {
		nvkm_error(subdev, "libos print is too large (%u bytes)\n",
			   rpc->libos_print_buf_size);
		return -E2BIG;
	}

	memcpy(gsp->blob_pmu.data, rpc->libos_print_buf, rpc->libos_print_buf_size);

	return 0;
}

/**
 * create_debugfs - create a blob debugfs entry
 * @gsp: gsp pointer
 * @name: name of this dentry
 * @blob: blob wrapper
 *
 * Creates a debugfs entry for a logging buffer with the name 'name'.
 */
static struct dentry *create_debugfs(struct nvkm_gsp *gsp, const char *name,
				     struct debugfs_blob_wrapper *blob)
{
	struct dentry *dent;

	dent = debugfs_create_blob(name, 0444, gsp->debugfs.parent, blob);
	if (IS_ERR(dent)) {
		nvkm_error(&gsp->subdev,
			   "failed to create %s debugfs entry\n", name);
		return NULL;
	}

	/*
	 * For some reason, debugfs_create_blob doesn't set the size of the
	 * dentry, so do that here.  See [1]
	 *
	 * [1] https://lore.kernel.org/r/linux-fsdevel/20240207200619.3354549-1-ttabi@nvidia.com/
	 */
	i_size_write(d_inode(dent), blob->size);

	return dent;
}

/**
 * r535_gsp_libos_debugfs_init - create logging debugfs entries
 * @gsp: gsp pointer
 *
 * Create the debugfs entries. This exposes the log buffers to userspace so
 * that an external tool can parse it.
 *
 * The 'logpmu' contains exception dumps from the PMU. It is written via an
 * RPC sent from GSP-RM and must be only 4KB. We create it here because it's
 * only useful if there is a debugfs entry to expose it. If we get the PMU
 * logging RPC and there is no debugfs entry, the RPC is just ignored.
 *
 * The blob_init, blob_rm, and blob_pmu objects can't be transient
 * because debugfs_create_blob doesn't copy them.
 *
 * NOTE: OpenRM loads the logging elf image and prints the log messages
 * in real-time. We may add that capability in the future, but that
 * requires loading ELF images that are not distributed with the driver and
 * adding the parsing code to Nouveau.
 *
 * Ideally, this should be part of nouveau_debugfs_init(), but that function
 * is called too late. We really want to create these debugfs entries before
 * r535_gsp_booter_load() is called, so that if GSP-RM fails to initialize,
 * there could still be a log to capture.
 */
static void
r535_gsp_libos_debugfs_init(struct nvkm_gsp *gsp)
{
	struct device *dev = gsp->subdev.device->dev;

	/* Create a new debugfs directory with a name unique to this GPU. */
	gsp->debugfs.parent = debugfs_create_dir(dev_name(dev), nouveau_debugfs_root);
	if (IS_ERR(gsp->debugfs.parent)) {
		nvkm_error(&gsp->subdev,
			   "failed to create %s debugfs root\n", dev_name(dev));
		return;
	}

	gsp->blob_init.data = gsp->loginit.data;
	gsp->blob_init.size = gsp->loginit.size;
	gsp->blob_intr.data = gsp->logintr.data;
	gsp->blob_intr.size = gsp->logintr.size;
	gsp->blob_rm.data = gsp->logrm.data;
	gsp->blob_rm.size = gsp->logrm.size;

	gsp->debugfs.init = create_debugfs(gsp, "loginit", &gsp->blob_init);
	if (!gsp->debugfs.init)
		goto error;

	gsp->debugfs.intr = create_debugfs(gsp, "logintr", &gsp->blob_intr);
	if (!gsp->debugfs.intr)
		goto error;

	gsp->debugfs.rm = create_debugfs(gsp, "logrm", &gsp->blob_rm);
	if (!gsp->debugfs.rm)
		goto error;

	/*
	 * Since the PMU buffer is copied from an RPC, it doesn't need to be
	 * a DMA buffer.
	 */
	gsp->blob_pmu.size = GSP_PAGE_SIZE;
	gsp->blob_pmu.data = kzalloc(gsp->blob_pmu.size, GFP_KERNEL);
	if (!gsp->blob_pmu.data)
		goto error;

	gsp->debugfs.pmu = create_debugfs(gsp, "logpmu", &gsp->blob_pmu);
	if (!gsp->debugfs.pmu) {
		kfree(gsp->blob_pmu.data);
		goto error;
	}

	i_size_write(d_inode(gsp->debugfs.init), gsp->blob_init.size);
	i_size_write(d_inode(gsp->debugfs.intr), gsp->blob_intr.size);
	i_size_write(d_inode(gsp->debugfs.rm), gsp->blob_rm.size);
	i_size_write(d_inode(gsp->debugfs.pmu), gsp->blob_pmu.size);

	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
			      r535_gsp_msg_libos_print, gsp);

	nvkm_debug(&gsp->subdev, "created debugfs GSP-RM logging entries\n");

	if (keep_gsp_logging) {
		nvkm_info(&gsp->subdev,
			  "logging buffers will be retained on failure\n");
	}

	return;

error:
	debugfs_remove(gsp->debugfs.parent);
	gsp->debugfs.parent = NULL;
}

#endif

static inline u64
r535_gsp_libos_id8(const char *name)
{
	u64 id = 0;

	for (int i = 0; i < sizeof(id) && *name; i++, name++)
		id = (id << 8) | *name;

	return id;
}

/**
 * create_pte_array() - creates a PTE array of a physically contiguous buffer
 * @ptes: pointer to the array
 * @addr: base address of physically contiguous buffer (GSP_PAGE_SIZE aligned)
 * @size: size of the buffer
 *
 * GSP-RM sometimes expects physically-contiguous buffers to have an array of
 * "PTEs" for each page in that buffer.  Although in theory that allows for
 * the buffer to be physically discontiguous, GSP-RM does not currently
 * support that.
 *
 * In this case, the PTEs are DMA addresses of each page of the buffer.  Since
 * the buffer is physically contiguous, calculating all the PTEs is simple
 * math.
 *
 * See memdescGetPhysAddrsForGpu()
 */
static void create_pte_array(u64 *ptes, dma_addr_t addr, size_t size)
{
	unsigned int num_pages = DIV_ROUND_UP_ULL(size, GSP_PAGE_SIZE);
	unsigned int i;

	for (i = 0; i < num_pages; i++)
		ptes[i] = (u64)addr + (i << GSP_PAGE_SHIFT);
}

/**
 * r535_gsp_libos_init() -- create the libos arguments structure
 * @gsp: gsp pointer
 *
 * The logging buffers are byte queues that contain encoded printf-like
 * messages from GSP-RM.  They need to be decoded by a special application
 * that can parse the buffers.
 *
 * The 'loginit' buffer contains logs from early GSP-RM init and
 * exception dumps.  The 'logrm' buffer contains the subsequent logs. Both are
 * written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE.
 *
 * The physical address map for the log buffer is stored in the buffer
 * itself, starting with offset 1. Offset 0 contains the "put" pointer (pp).
 * Initially, pp is equal to 0. If the buffer has valid logging data in it,
 * then pp points to index into the buffer where the next logging entry will
 * be written. Therefore, the logging data is valid if:
 *   1 <= pp < sizeof(buffer)/sizeof(u64)
 *
 * The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
 * configured for a larger page size (e.g. 64K pages), we need to give
 * the GSP an array of 4K pages. Fortunately, since the buffer is
 * physically contiguous, it's simple math to calculate the addresses.
 *
 * The buffers must be a multiple of GSP_PAGE_SIZE.  GSP-RM also currently
 * ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
 * buffers to be physically contiguous anyway.
 *
 * The memory allocated for the arguments must remain until the GSP sends the
 * init_done RPC.
 *
 * See _kgspInitLibosLoggingStructures (allocates memory for buffers)
 * See kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
 */
static int
r535_gsp_libos_init(struct nvkm_gsp *gsp)
{
	LibosMemoryRegionInitArgument *args;
	int ret;

	ret = nvkm_gsp_mem_ctor(gsp, 0x1000, &gsp->libos);
	if (ret)
		return ret;

	args = gsp->libos.data;

	ret = nvkm_gsp_mem_ctor(gsp, 0x10000, &gsp->loginit);
	if (ret)
		return ret;

	args[0].id8  = r535_gsp_libos_id8("LOGINIT");
	args[0].pa   = gsp->loginit.addr;
	args[0].size = gsp->loginit.size;
	args[0].kind = LIBOS_MEMORY_REGION_CONTIGUOUS;
	args[0].loc  = LIBOS_MEMORY_REGION_LOC_SYSMEM;
	create_pte_array(gsp->loginit.data + sizeof(u64), gsp->loginit.addr, gsp->loginit.size);

	ret = nvkm_gsp_mem_ctor(gsp, 0x10000, &gsp->logintr);
	if (ret)
		return ret;

	args[1].id8  = r535_gsp_libos_id8("LOGINTR");
	args[1].pa   = gsp->logintr.addr;
	args[1].size = gsp->logintr.size;
	args[1].kind = LIBOS_MEMORY_REGION_CONTIGUOUS;
	args[1].loc  = LIBOS_MEMORY_REGION_LOC_SYSMEM;
	create_pte_array(gsp->logintr.data + sizeof(u64), gsp->logintr.addr, gsp->logintr.size);

	ret = nvkm_gsp_mem_ctor(gsp, 0x10000, &gsp->logrm);
	if (ret)
		return ret;

	args[2].id8  = r535_gsp_libos_id8("LOGRM");
	args[2].pa   = gsp->logrm.addr;
	args[2].size = gsp->logrm.size;
	args[2].kind = LIBOS_MEMORY_REGION_CONTIGUOUS;
	args[2].loc  = LIBOS_MEMORY_REGION_LOC_SYSMEM;
	create_pte_array(gsp->logrm.data + sizeof(u64), gsp->logrm.addr, gsp->logrm.size);

	ret = r535_gsp_rmargs_init(gsp, false);
	if (ret)
		return ret;

	args[3].id8  = r535_gsp_libos_id8("RMARGS");
	args[3].pa   = gsp->rmargs.addr;
	args[3].size = gsp->rmargs.size;
	args[3].kind = LIBOS_MEMORY_REGION_CONTIGUOUS;
	args[3].loc  = LIBOS_MEMORY_REGION_LOC_SYSMEM;

#ifdef CONFIG_DEBUG_FS
	r535_gsp_libos_debugfs_init(gsp);
#endif

	return 0;
}

void
nvkm_gsp_sg_free(struct nvkm_device *device, struct sg_table *sgt)
{
	struct scatterlist *sgl;
	int i;

	dma_unmap_sgtable(device->dev, sgt, DMA_BIDIRECTIONAL, 0);

	for_each_sgtable_sg(sgt, sgl, i) {
		struct page *page = sg_page(sgl);

		__free_page(page);
	}

	sg_free_table(sgt);
}

int
nvkm_gsp_sg(struct nvkm_device *device, u64 size, struct sg_table *sgt)
{
	const u64 pages = DIV_ROUND_UP(size, PAGE_SIZE);
	struct scatterlist *sgl;
	int ret, i;

	ret = sg_alloc_table(sgt, pages, GFP_KERNEL);
	if (ret)
		return ret;

	for_each_sgtable_sg(sgt, sgl, i) {
		struct page *page = alloc_page(GFP_KERNEL);

		if (!page) {
			nvkm_gsp_sg_free(device, sgt);
			return -ENOMEM;
		}

		sg_set_page(sgl, page, PAGE_SIZE, 0);
	}

	ret = dma_map_sgtable(device->dev, sgt, DMA_BIDIRECTIONAL, 0);
	if (ret)
		nvkm_gsp_sg_free(device, sgt);

	return ret;
}

static void
nvkm_gsp_radix3_dtor(struct nvkm_gsp *gsp, struct nvkm_gsp_radix3 *rx3)
{
	nvkm_gsp_sg_free(gsp->subdev.device, &rx3->lvl2);
	nvkm_gsp_mem_dtor(&rx3->lvl1);
	nvkm_gsp_mem_dtor(&rx3->lvl0);
}

/**
 * nvkm_gsp_radix3_sg - build a radix3 table from a S/G list
 * @gsp: gsp pointer
 * @sgt: S/G list to traverse
 * @size: size of the image, in bytes
 * @rx3: radix3 array to update
 *
 * The GSP uses a three-level page table, called radix3, to map the firmware.
 * Each 64-bit "pointer" in the table is either the bus address of an entry in
 * the next table (for levels 0 and 1) or the bus address of the next page in
 * the GSP firmware image itself.
 *
 * Level 0 contains a single entry in one page that points to the first page
 * of level 1.
 *
 * Level 1, since it's also only one page in size, contains up to 512 entries,
 * one for each page in Level 2.
 *
 * Level 2 can be up to 512 pages in size, and each of those entries points to
 * the next page of the firmware image.  Since there can be up to 512*512
 * pages, that limits the size of the firmware to 512*512*GSP_PAGE_SIZE = 1GB.
 *
 * Internally, the GSP has its window into system memory, but the base
 * physical address of the aperture is not 0.  In fact, it varies depending on
 * the GPU architecture.  Since the GPU is a PCI device, this window is
 * accessed via DMA and is therefore bound by IOMMU translation.  The end
 * result is that GSP-RM must translate the bus addresses in the table to GSP
 * physical addresses.  All this should happen transparently.
 *
 * Returns 0 on success, or negative error code
 *
 * See kgspCreateRadix3_IMPL
 */
static int
nvkm_gsp_radix3_sg(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size,
		   struct nvkm_gsp_radix3 *rx3)
{
	struct sg_dma_page_iter sg_dma_iter;
	struct scatterlist *sg;
	size_t bufsize;
	u64 *pte;
	int ret, i, page_idx = 0;

	ret = nvkm_gsp_mem_ctor(gsp, GSP_PAGE_SIZE, &rx3->lvl0);
	if (ret)
		return ret;

	ret = nvkm_gsp_mem_ctor(gsp, GSP_PAGE_SIZE, &rx3->lvl1);
	if (ret)
		goto lvl1_fail;

	// Allocate level 2
	bufsize = ALIGN((size / GSP_PAGE_SIZE) * sizeof(u64), GSP_PAGE_SIZE);
	ret = nvkm_gsp_sg(gsp->subdev.device, bufsize, &rx3->lvl2);
	if (ret)
		goto lvl2_fail;

	// Write the bus address of level 1 to level 0
	pte = rx3->lvl0.data;
	*pte = rx3->lvl1.addr;

	// Write the bus address of each page in level 2 to level 1
	pte = rx3->lvl1.data;
	for_each_sgtable_dma_page(&rx3->lvl2, &sg_dma_iter, 0)
		*pte++ = sg_page_iter_dma_address(&sg_dma_iter);

	// Finally, write the bus address of each page in sgt to level 2
	for_each_sgtable_sg(&rx3->lvl2, sg, i) {
		void *sgl_end;

		pte = sg_virt(sg);
		sgl_end = (void *)pte + sg->length;

		for_each_sgtable_dma_page(sgt, &sg_dma_iter, page_idx) {
			*pte++ = sg_page_iter_dma_address(&sg_dma_iter);
			page_idx++;

			// Go to the next scatterlist for level 2 if we've reached the end
			if ((void *)pte >= sgl_end)
				break;
		}
	}

	if (ret) {
lvl2_fail:
		nvkm_gsp_mem_dtor(&rx3->lvl1);
lvl1_fail:
		nvkm_gsp_mem_dtor(&rx3->lvl0);
	}

	return ret;
}

static u32
r535_gsp_sr_data_size(struct nvkm_gsp *gsp)
{
	GspFwWprMeta *meta = gsp->wpr_meta.data;

	return meta->gspFwWprEnd - meta->gspFwWprStart;
}

int
r535_gsp_fini(struct nvkm_gsp *gsp, bool suspend)
{
	struct nvkm_rm *rm = gsp->rm;
	int ret;

	if (suspend) {
		u32 len = rm->api->gsp->sr_data_size(gsp);
		GspFwSRMeta *sr;

		ret = nvkm_gsp_sg(gsp->subdev.device, len, &gsp->sr.sgt);
		if (ret)
			return ret;

		ret = nvkm_gsp_radix3_sg(gsp, &gsp->sr.sgt, len, &gsp->sr.radix3);
		if (ret)
			return ret;

		ret = nvkm_gsp_mem_ctor(gsp, sizeof(*sr), &gsp->sr.meta);
		if (ret)
			return ret;

		sr = gsp->sr.meta.data;
		sr->magic = GSP_FW_SR_META_MAGIC;
		sr->revision = GSP_FW_SR_META_REVISION;
		sr->sysmemAddrOfSuspendResumeData = gsp->sr.radix3.lvl0.addr;
		sr->sizeOfSuspendResumeData = len;

		ret = rm->api->fbsr->suspend(gsp);
		if (ret) {
			nvkm_gsp_mem_dtor(&gsp->sr.meta);
			nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3);
			nvkm_gsp_sg_free(gsp->subdev.device, &gsp->sr.sgt);
			return ret;
		}

		/*
		 * TODO: Debug the GSP firmware / RPC handling to find out why
		 * without this Turing (but none of the other architectures)
		 * ends up resetting all channels after resume.
		 */
		msleep(50);
	}

	ret = r535_gsp_rpc_unloading_guest_driver(gsp, suspend);
	if (WARN_ON(ret))
		return ret;

	nvkm_msec(gsp->subdev.device, 2000,
		if (nvkm_falcon_rd32(&gsp->falcon, 0x040) == 0x80000000)
			break;
	);

	gsp->running = false;
	return 0;
}

int
r535_gsp_init(struct nvkm_gsp *gsp)
{
	int ret;

	nvkm_falcon_wr32(&gsp->falcon, 0x080, gsp->boot.app_version);

	if (WARN_ON(!nvkm_falcon_riscv_active(&gsp->falcon)))
		return -EIO;

	ret = r535_gsp_rpc_poll(gsp, NV_VGPU_MSG_EVENT_GSP_INIT_DONE);
	if (ret)
		goto done;

	gsp->running = true;

done:
	if (gsp->sr.meta.data) {
		gsp->rm->api->fbsr->resume(gsp);

		nvkm_gsp_mem_dtor(&gsp->sr.meta);
		nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3);
		nvkm_gsp_sg_free(gsp->subdev.device, &gsp->sr.sgt);
		return ret;
	}

	if (ret == 0)
		ret = r535_gsp_postinit(gsp);

	return ret;
}

static int
r535_gsp_rm_boot_ctor(struct nvkm_gsp *gsp)
{
	const struct firmware *fw = gsp->fws.bl;
	const struct nvfw_bin_hdr *hdr;
	RM_RISCV_UCODE_DESC *desc;
	int ret;

	hdr = nvfw_bin_hdr(&gsp->subdev, fw->data);
	desc = (void *)fw->data + hdr->header_offset;

	ret = nvkm_gsp_mem_ctor(gsp, hdr->data_size, &gsp->boot.fw);
	if (ret)
		return ret;

	memcpy(gsp->boot.fw.data, fw->data + hdr->data_offset, hdr->data_size);

	gsp->boot.code_offset = desc->monitorCodeOffset;
	gsp->boot.data_offset = desc->monitorDataOffset;
	gsp->boot.manifest_offset = desc->manifestOffset;
	gsp->boot.app_version = desc->appVersion;
	return 0;
}

static const struct nvkm_firmware_func
r535_gsp_fw = {
	.type = NVKM_FIRMWARE_IMG_SGT,
};

static int
r535_gsp_elf_section(struct nvkm_gsp *gsp, const char *name, const u8 **pdata, u64 *psize)
{
	const u8 *img = gsp->fws.rm->data;
	const struct elf64_hdr *ehdr = (const struct elf64_hdr *)img;
	const struct elf64_shdr *shdr = (const struct elf64_shdr *)&img[ehdr->e_shoff];
	const char *names = &img[shdr[ehdr->e_shstrndx].sh_offset];

	for (int i = 0; i < ehdr->e_shnum; i++, shdr++) {
		if (!strcmp(&names[shdr->sh_name], name)) {
			*pdata = &img[shdr->sh_offset];
			*psize = shdr->sh_size;
			return 0;
		}
	}

	nvkm_error(&gsp->subdev, "section '%s' not found\n", name);
	return -ENOENT;
}

#ifdef CONFIG_DEBUG_FS

struct r535_gsp_log {
	struct nvif_log log;

	/*
	 * Logging buffers in debugfs. The wrapper objects need to remain
	 * in memory until the dentry is deleted.
	 */
	struct dentry *debugfs_logging_dir;
	struct debugfs_blob_wrapper blob_init;
	struct debugfs_blob_wrapper blob_intr;
	struct debugfs_blob_wrapper blob_rm;
	struct debugfs_blob_wrapper blob_pmu;
};

/**
 * r535_debugfs_shutdown - delete GSP-RM logging buffers for one GPU
 * @_log: nvif_log struct for this GPU
 *
 * Called when the driver is shutting down, to clean up the retained GSP-RM
 * logging buffers.
 */
static void r535_debugfs_shutdown(struct nvif_log *_log)
{
	struct r535_gsp_log *log = container_of(_log, struct r535_gsp_log, log);

	debugfs_remove(log->debugfs_logging_dir);

	kfree(log->blob_init.data);
	kfree(log->blob_intr.data);
	kfree(log->blob_rm.data);
	kfree(log->blob_pmu.data);

	/* We also need to delete the list object */
	kfree(log);
}

/**
 * is_empty - return true if the logging buffer was never written to
 * @b: blob wrapper with ->data field pointing to logging buffer
 *
 * The first 64-bit field of loginit, and logintr, and logrm is the 'put'
 * pointer, and it is initialized to 0. It's a dword-based index into the
 * circular buffer, indicating where the next printf write will be made.
 *
 * If the pointer is still 0 when GSP-RM is shut down, that means that the
 * buffer was never written to, so it can be ignored.
 *
 * This test also works for logpmu, even though it doesn't have a put pointer.
 */
static bool is_empty(const struct debugfs_blob_wrapper *b)
{
	u64 *put = b->data;

	return put ? (*put == 0) : true;
}

/**
 * r535_gsp_copy_log - preserve the logging buffers in a blob
 * @parent: the top-level dentry for this GPU
 * @name: name of debugfs entry to create
 * @s: original wrapper object to copy from
 * @t: new wrapper object to copy to
 *
 * When GSP shuts down, the nvkm_gsp object and all its memory is deleted.
 * To preserve the logging buffers, the buffers need to be copied, but only
 * if they actually have data.
 */
static int r535_gsp_copy_log(struct dentry *parent,
			     const char *name,
			     const struct debugfs_blob_wrapper *s,
			     struct debugfs_blob_wrapper *t)
{
	struct dentry *dent;
	void *p;

	if (is_empty(s))
		return 0;

	/* The original buffers will be deleted */
	p = kmemdup(s->data, s->size, GFP_KERNEL);
	if (!p)
		return -ENOMEM;

	t->data = p;
	t->size = s->size;

	dent = debugfs_create_blob(name, 0444, parent, t);
	if (IS_ERR(dent)) {
		kfree(p);
		memset(t, 0, sizeof(*t));
		return PTR_ERR(dent);
	}

	i_size_write(d_inode(dent), t->size);

	return 0;
}

/**
 * r535_gsp_retain_logging - copy logging buffers to new debugfs root
 * @gsp: gsp pointer
 *
 * If keep_gsp_logging is enabled, then we want to preserve the GSP-RM logging
 * buffers and their debugfs entries, but all those objects would normally
 * deleted if GSP-RM fails to load.
 *
 * To preserve the logging buffers, we need to:
 *
 * 1) Allocate new buffers and copy the logs into them, so that the original
 * DMA buffers can be released.
 *
 * 2) Preserve the directories.  We don't need to save single dentries because
 * we're going to delete the parent when the
 *
 * If anything fails in this process, then all the dentries need to be
 * deleted.  We don't need to deallocate the original logging buffers because
 * the caller will do that regardless.
 */
static void r535_gsp_retain_logging(struct nvkm_gsp *gsp)
{
	struct device *dev = gsp->subdev.device->dev;
	struct r535_gsp_log *log = NULL;
	int ret;

	if (!keep_gsp_logging || !gsp->debugfs.parent) {
		/* Nothing to do */
		goto exit;
	}

	/* Check to make sure at least one buffer has data. */
	if (is_empty(&gsp->blob_init) && is_empty(&gsp->blob_intr) &&
	    is_empty(&gsp->blob_rm) && is_empty(&gsp->blob_rm)) {
		nvkm_warn(&gsp->subdev, "all logging buffers are empty\n");
		goto exit;
	}

	log = kzalloc(sizeof(*log), GFP_KERNEL);
	if (!log)
		goto error;

	/*
	 * Since the nvkm_gsp object is going away, the debugfs_blob_wrapper
	 * objects are also being deleted, which means the dentries will no
	 * longer be valid.  Delete the existing entries so that we can create
	 * new ones with the same name.
	 */
	debugfs_remove(gsp->debugfs.init);
	debugfs_remove(gsp->debugfs.intr);
	debugfs_remove(gsp->debugfs.rm);
	debugfs_remove(gsp->debugfs.pmu);

	ret = r535_gsp_copy_log(gsp->debugfs.parent, "loginit", &gsp->blob_init, &log->blob_init);
	if (ret)
		goto error;

	ret = r535_gsp_copy_log(gsp->debugfs.parent, "logintr", &gsp->blob_intr, &log->blob_intr);
	if (ret)
		goto error;

	ret = r535_gsp_copy_log(gsp->debugfs.parent, "logrm", &gsp->blob_rm, &log->blob_rm);
	if (ret)
		goto error;

	ret = r535_gsp_copy_log(gsp->debugfs.parent, "logpmu", &gsp->blob_pmu, &log->blob_pmu);
	if (ret)
		goto error;

	/* The nvkm_gsp object is going away, so save the dentry */
	log->debugfs_logging_dir = gsp->debugfs.parent;

	log->log.shutdown = r535_debugfs_shutdown;
	list_add(&log->log.entry, &gsp_logs.head);

	nvkm_warn(&gsp->subdev,
		  "logging buffers migrated to /sys/kernel/debug/nouveau/%s\n",
		  dev_name(dev));

	return;

error:
	nvkm_warn(&gsp->subdev, "failed to migrate logging buffers\n");

exit:
	debugfs_remove(gsp->debugfs.parent);

	if (log) {
		kfree(log->blob_init.data);
		kfree(log->blob_intr.data);
		kfree(log->blob_rm.data);
		kfree(log->blob_pmu.data);
		kfree(log);
	}
}

#endif

/**
 * r535_gsp_libos_debugfs_fini - cleanup/retain log buffers on shutdown
 * @gsp: gsp pointer
 *
 * If the log buffers are exposed via debugfs, the data for those entries
 * needs to be cleaned up when the GSP device shuts down.
 */
static void
r535_gsp_libos_debugfs_fini(struct nvkm_gsp __maybe_unused *gsp)
{
#ifdef CONFIG_DEBUG_FS
	r535_gsp_retain_logging(gsp);

	/*
	 * Unlike the other buffers, the PMU blob is a kmalloc'd buffer that
	 * exists only if the debugfs entries were created.
	 */
	kfree(gsp->blob_pmu.data);
	gsp->blob_pmu.data = NULL;
#endif
}

void
r535_gsp_dtor(struct nvkm_gsp *gsp)
{
	idr_destroy(&gsp->client_id.idr);
	mutex_destroy(&gsp->client_id.mutex);

	nvkm_gsp_radix3_dtor(gsp, &gsp->radix3);
	nvkm_gsp_mem_dtor(&gsp->sig);
	nvkm_firmware_dtor(&gsp->fw);

	nvkm_falcon_fw_dtor(&gsp->booter.unload);
	nvkm_falcon_fw_dtor(&gsp->booter.load);

	nvkm_gsp_mem_dtor(&gsp->fmc.args);
	kfree(gsp->fmc.sig);
	kfree(gsp->fmc.pkey);
	kfree(gsp->fmc.hash);
	nvkm_gsp_mem_dtor(&gsp->fmc.fw);

	mutex_destroy(&gsp->msgq.mutex);
	mutex_destroy(&gsp->cmdq.mutex);

	nvkm_gsp_dtor_fws(gsp);

	nvkm_gsp_mem_dtor(&gsp->rmargs);
	nvkm_gsp_mem_dtor(&gsp->wpr_meta);
	nvkm_gsp_mem_dtor(&gsp->shm.mem);

	r535_gsp_libos_debugfs_fini(gsp);

	nvkm_gsp_mem_dtor(&gsp->loginit);
	nvkm_gsp_mem_dtor(&gsp->logintr);
	nvkm_gsp_mem_dtor(&gsp->logrm);
}

static void
r535_gsp_drop_send_user_shared_data(struct nvkm_gsp *gsp)
{
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_GSP_SEND_USER_SHARED_DATA, NULL, NULL);
}

int
r535_gsp_oneinit(struct nvkm_gsp *gsp)
{
	struct nvkm_device *device = gsp->subdev.device;
	const struct nvkm_rm_api *rmapi = gsp->rm->api;
	const u8 *data;
	u64 size;
	int ret;

	mutex_init(&gsp->cmdq.mutex);
	mutex_init(&gsp->msgq.mutex);

	/* Load GSP firmware from ELF image into DMA-accessible memory. */
	ret = r535_gsp_elf_section(gsp, ".fwimage", &data, &size);
	if (ret)
		return ret;

	ret = nvkm_firmware_ctor(&r535_gsp_fw, "gsp-rm", device, data, size, &gsp->fw);
	if (ret)
		return ret;

	/* Load relevant signature from ELF image. */
	ret = r535_gsp_elf_section(gsp, gsp->func->sig_section, &data, &size);
	if (ret)
		return ret;

	ret = nvkm_gsp_mem_ctor(gsp, ALIGN(size, 256), &gsp->sig);
	if (ret)
		return ret;

	memcpy(gsp->sig.data, data, size);

	/* Build radix3 page table for ELF image. */
	ret = nvkm_gsp_radix3_sg(gsp, &gsp->fw.mem.sgt, gsp->fw.len, &gsp->radix3);
	if (ret)
		return ret;

	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
			      r535_gsp_msg_run_cpu_sequencer, gsp);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_POST_EVENT, r535_gsp_msg_post_event, gsp);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_RC_TRIGGERED, rmapi->fifo->rc_triggered, gsp);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
			      r535_gsp_msg_mmu_fault_queued, gsp);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_OS_ERROR_LOG, r535_gsp_msg_os_error_log, gsp);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE, NULL, NULL);
	r535_gsp_msg_ntfy_add(gsp, NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT, NULL, NULL);
	if (rmapi->gsp->drop_send_user_shared_data)
		rmapi->gsp->drop_send_user_shared_data(gsp);
	if (rmapi->gsp->drop_post_nocat_record)
		rmapi->gsp->drop_post_nocat_record(gsp);

	ret = r535_gsp_rm_boot_ctor(gsp);
	if (ret)
		return ret;

	/* Release FW images - we've copied them to DMA buffers now. */
	nvkm_gsp_dtor_fws(gsp);

	ret = r535_gsp_libos_init(gsp);
	if (WARN_ON(ret))
		return ret;

	ret = rmapi->gsp->set_system_info(gsp);
	if (WARN_ON(ret))
		return ret;

	ret = r535_gsp_rpc_set_registry(gsp);
	if (WARN_ON(ret))
		return ret;

	mutex_init(&gsp->client_id.mutex);
	idr_init(&gsp->client_id.idr);
	return 0;
}

const struct nvkm_rm_api_gsp
r535_gsp = {
	.set_rmargs = r535_gsp_set_rmargs,
	.set_system_info = r535_gsp_set_system_info,
	.get_static_info = r535_gsp_get_static_info,
	.xlat_mc_engine_idx = r535_gsp_xlat_mc_engine_idx,
	.drop_send_user_shared_data = r535_gsp_drop_send_user_shared_data,
	.sr_data_size = r535_gsp_sr_data_size,
};
